package com.umeox.watch.prayertime.utils

import android.content.res.AssetManager
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.umeox.moto.common.log.Logger
import com.umeox.moto.common.utils.AppUtils
import com.umeox.watch.prayertime.R
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.text.SimpleDateFormat
import java.util.*
import kotlin.math.*

class PrayTime2 {

    //新增固定惯例
    private val ingushetiaData: Map<String, MutableList<String>>
    private val bruneikheuData: Map<String, MutableList<String>>

    // ---------------------- Global Variables --------------------
    var calcMethod // caculation method
            = 0
    var asrJuristic // Juristic method for Asr
            = 0
    var dhuhrMinutes // minutes after mid-day for Dhuhr
            = 0
    var adjustHighLats // adjusting method for higher latitudes
            = 0
    var timeFormat // time format
            = 0
    var lat // latitude
            = 0.0
    var lng // longitude
            = 0.0
    var timeZone // time-zone
            = 0.0
    var jDate // Julian date
            = 0.0

    // ------------------------------------------------------------
    // Calculation Methods

    var jafari // Ithna Ashari
            = 0
    var karachi // University of Islamic Sciences, Karachi
            = 0
    var iSNA // Islamic Society of North America (ISNA)
            = 0
    var mWL // Muslim World League (MWL)
            = 0
    var makkah // Umm al-Qura, Makkah
            = 0
    var egypt // Egyptian General Authority of Survey
            = 0

    var tehran // Institute of Geophysics, University of Tehran
            = 0

    var muis // Majlis Ugama Islam Singapura.
            = 0

    //ring 2.0 新增惯例
    var jakim // Calculation method from Jabatan Kemajuan Islam Malaysia.
            = 0

    var kemenag // Calculation method from Kementerian Agama Republik Indonesia.
            = 0

    var diyanet // Calculation method from Turkey's Diyanet İşleri Başkanlığı.
            = 0

    var dsmr // Calculation method from Spiritual Administration of Muslims of Russia.
            = 0

    var dumsr // Calculation method from The Muslim Spiritual Board of Tatarstan.
            = 0

    var gaiae // Calculation method from General Authority of Islamic Affairs And Endowments.
            = 0

    var gulf // Calculation method that often used by countries in Gulf region like UAE and Kuwait.
            = 0

    var algeria // Calculation method from Algerian Ministry of Religious Affairs and Wakfs.
            = 0

    var uoif // Calculation method from Union Des Organisations Islamiques De France.
            = 0

    var france15 // Calculation method for France region with Fajr and Isha both at 15°.
            = 0

    var france18 // Another calculation method for France region with Fajr and Isha both at 18°.
            = 0

    var tunisia // Calculation method from Tunisian Ministry of Religious Affairs.
            = 0

    var london // Calculation method from London Central Mosque Trust &amp; The Islamic Cultural Centre.
            = 0

    //新增固定惯例 Ingushetia 固定值100
    var ingushetia = 100
    var brunei_kheu = 101

    var custom // Custom Setting
            = 0

    // Juristic Methods
    var shafii // Shafii (standard)
            = 0
    var hanafi // Hanafi
            = 0

    // Adjusting Methods for Higher Latitudes
    var none // No adjustment
            = 0
    var midNight // middle of night
            = 0
    var oneSeventh // 1/7th of night
            = 0
    var angleBased // angle/60th of night
            = 0

    // Time Formats
    var time24 // 24-hour format
            = 0
    var time12 // 12-hour format
            = 0
    var time12NS // 12-hour format with no suffix
            = 0
    var floating // floating point number
            = 0

    // Time Names
    var timeNames: ArrayList<Int>
    val InvalidTime // The string used for invalid times
            : String

    // --------------------- Technical Settings --------------------
    var numIterations // number of iterations needed to compute times
            = 0

    // ------------------- Calc Method Parameters --------------------
    val methodParams: HashMap<Int, DoubleArray>

    /*
     * this.methodParams[methodNum] = new Array(fa, ms, mv, is, iv);
     *
     * fa : fajr angle ms : maghrib selector (0 = angle; 1 = minutes after
     * sunset) mv : maghrib parameter value (in angle or minutes) is : isha
     * selector (0 = angle; 1 = minutes after maghrib) iv : isha parameter value
     * (in angle or minutes)
     */
    lateinit var prayerTimesCurrent: DoubleArray
    var offsets: IntArray


    /**
     *各个礼拜时间
     */

    var fajrTime = ""
    var sunriseTime = ""
    var dhuhrTime = ""
    var asrTime = ""
    var sunsetTime = ""
    var maghribTime = ""
    var ishaTime = ""
    var midnight = ""


    // ---------------------- Trigonometric Functions -----------------------
    // range reduce angle in degrees.
    private fun fixangle(a: Double): Double {
        var a = a
        a = a - 360 * Math.floor(a / 360.0)
        a = if (a < 0) a + 360 else a
        return a
    }

    // range reduce hours to 0..23
    private fun fixhour(a: Double): Double {
        var a = a
        a = a - 24.0 * Math.floor(a / 24.0)
        a = if (a < 0) a + 24 else a
        return a
    }

    // radian to degree
    private fun radiansToDegrees(alpha: Double): Double {
        return alpha * 180.0 / Math.PI
    }

    // deree to radian
    fun DegreesToRadians(alpha: Double): Double {
        return alpha * Math.PI / 180.0
    }

    // degree sin
    private fun dsin(d: Double): Double {
        return sin(DegreesToRadians(d))
    }

    // degree cos
    private fun dcos(d: Double): Double {
        return cos(DegreesToRadians(d))
    }

    // degree tan
    fun dtan(d: Double): Double {
        return tan(DegreesToRadians(d))
    }

    // degree arcsin
    private fun darcsin(x: Double): Double {
        val `val` = asin(x)
        return radiansToDegrees(`val`)
    }

    // degree arccos
    private fun darccos(x: Double): Double {
        val `val` = acos(x)
        return radiansToDegrees(`val`)
    }

    // degree arctan
    private fun darctan(x: Double): Double {
        val `val` = atan(x)
        return radiansToDegrees(`val`)
    }

    // degree arctan2
    fun darctan2(y: Double, x: Double): Double {
        val `val` = atan2(y, x)
        return radiansToDegrees(`val`)
    }

    // degree arccot
    fun darccot(x: Double): Double {
        val `val` = atan2(1.0, x)
        return radiansToDegrees(`val`)
    }

    // ---------------------- Time-Zone Functions -----------------------
    // compute local time-zone for a specific date
    val timeZone1: Double
        get() {
            val timez = TimeZone.getDefault()
            return timez.rawOffset / 1000.0 / 3600
        }

    // compute base time-zone of the system
    val baseTimeZone: Double
        get() {
            val timez = TimeZone.getDefault()
            return timez.rawOffset / 1000.0 / 3600
        }

    // detect daylight saving in a given date
    fun detectDaylightSaving(): Double {
        val timez = TimeZone.getDefault()
        return timez.dstSavings.toDouble()
    }

    // ---------------------- Julian Date Functions -----------------------
    // calculate julian date from a calendar date
    fun julianDate(year: Int, month: Int, day: Int): Double {
        var year = year
        var month = month
        if (month <= 2) {
            year -= 1
            month += 12
        }
        val A = floor(year / 100.0)
        val B = 2 - A + floor(A / 4.0)
        return (floor(365.25 * (year + 4716))
                + floor(30.6001 * (month + 1)) + day + B) - 1524.5
    }

    // convert a calendar date to julian date (second method)
    fun calcJD(year: Int, month: Int, day: Int): Double {
        val J1970 = 2440588.0
        val date = Date(year, month - 1, day)
        val ms = date.time.toDouble() // # of milliseconds since midnight Jan 1,
        // 1970
        val days = floor(ms / (1000.0 * 60.0 * 60.0 * 24.0))
        return J1970 + days - 0.5
    }

    // ---------------------- Calculation Functions -----------------------
    // References:
    // http://www.ummah.net/astronomy/saltime
    // http://aa.usno.navy.mil/faq/docs/SunApprox.html
    // compute declination angle of sun and equation of time
    fun sunPosition(jd: Double): DoubleArray {
        val D = jd - 2451545
        val g = fixangle(357.529 + 0.98560028 * D)
        val q = fixangle(280.459 + 0.98564736 * D)
        val L = fixangle(q + 1.915 * dsin(g) + 0.020 * dsin(2 * g))

        // double R = 1.00014 - 0.01671 * [self dcos:g] - 0.00014 * [self dcos:
        // (2*g)];
        val e = 23.439 - 0.00000036 * D
        val d = darcsin(dsin(e) * dsin(L))
        var RA = darctan2(dcos(e) * dsin(L), dcos(L)) / 15.0
        RA = fixhour(RA)
        val EqT = q / 15.0 - RA
        val sPosition = DoubleArray(2)
        sPosition[0] = d
        sPosition[1] = EqT
        return sPosition
    }

    // compute equation of time
    fun equationOfTime(jd: Double): Double {
        return sunPosition(jd)[1]
    }

    // compute declination angle of sun
    fun sunDeclination(jd: Double): Double {
        return sunPosition(jd)[0]
    }

    // compute mid-day (Dhuhr, Zawal) time
    fun computeMidDay(t: Double): Double {
        val T = equationOfTime(jDate + t)
        return fixhour(12 - T)
    }

    // compute time for a given angle G
    fun computeTime(G: Double, t: Double): Double {
        val D = sunDeclination(jDate + t)
        val Z = computeMidDay(t)
        val Beg = -dsin(G) - dsin(D) * dsin(lat)
        val Mid = dcos(D) * dcos(lat)
        val V = darccos(Beg / Mid) / 15.0
        return Z + if (G > 90) -V else V
    }

    // compute the time of Asr
    // Shafii: step=1, Hanafi: step=2
    fun computeAsr(step: Double, t: Double): Double {
        val D = sunDeclination(jDate + t)
        val G = -darccot(step + dtan(Math.abs(lat - D)))
        return computeTime(G, t)
    }

    // ---------------------- Misc Functions -----------------------
    // compute the difference between two times
    fun timeDiff(time1: Double, time2: Double): Double {
        return fixhour(time2 - time1)
    }

    // -------------------- Interface Functions --------------------
    // return prayer times for a given date
    fun getDatePrayerTimes(
        year: Int, month: Int, day: Int,
        latitude: Double, longitude: Double, tZone: Double, type: Int,
    ): MutableList<String> {

        //如果是固定惯例单独做处理
        if (calcMethod == ingushetia || calcMethod == brunei_kheu) {

            val instance = Calendar.getInstance()
            instance.set(year, month - 1, day)
            instance.add(Calendar.DAY_OF_MONTH, 1)

            val key = "$month-$day"
            val timeArray = if (calcMethod == ingushetia) {
                ingushetiaData[key]
            } else {
                bruneikheuData[key]
            }
            val tomorrowKey = "${instance[Calendar.MONTH] + 1}-${instance[Calendar.DAY_OF_MONTH]}"
            val tomorrowArray = if (calcMethod == ingushetia) {
                ingushetiaData[tomorrowKey]
            } else {
                bruneikheuData[tomorrowKey]
            }
            Logger.e("timeArray", timeArray.toString())
            timeArray?.forEachIndexed { index, s ->
                val toInt = s.split(":")[0].toInt()
                val timeType = if (toInt >= 12) {
                    "PM"
                } else {
                    "AM"
                }
                when (index) {
                    0 -> fajrTime = "$s$timeType"
                    1 -> sunriseTime = "$s$timeType"
                    2 -> dhuhrTime = "$s$timeType"
                    3 -> {
                        asrTime = "$s$timeType"
                        sunsetTime = "$s$timeType"
                    }
                    4 -> maghribTime = "$s$timeType"
                    5 -> ishaTime = "$s$timeType"
                }
            }


            //计算午夜
            tomorrowArray?.let {
                val sunrise = it[1]
                val sunset = timeArray!![4]

                val sunriseHour = sunrise.split(":")[0].toInt() + 24
                val sunriseMin = sunrise.split(":")[1].toInt()

                val sunsetHour = sunset.split(":")[0].toInt()
                val sunsetMin = sunset.split(":")[1].toInt()

                var difHour = (sunriseHour - sunsetHour)
                var difMin = sunriseMin - sunsetMin
                if (difMin < 0) {
                    difHour -= 1
                    difMin += 60
                }

                val sumMin = difHour * 60 + difMin
                val offsetMin = sumMin / 2

                var midnightHour = sunsetHour + offsetMin / 60
                var midnightMin = sunsetMin + offsetMin % 60

                if (midnightMin >= 60) {
                    midnightHour += 1
                    midnightMin -= 60
                }

                midnight = if (midnightHour >= 24) {
                    "${String.format(Locale.ENGLISH, "%02d", midnightHour - 24)}:${
                        String.format(
                            Locale.ENGLISH,
                            "%02d",
                            midnightMin
                        )
                    }AM"
                } else {
                    "${
                        String.format(
                            Locale.ENGLISH,
                            "%02d",
                            midnightHour
                        )
                    }:${String.format(Locale.ENGLISH, "%02d", midnightMin)}PM"
                }
            }

            return mutableListOf(
                fajrTime,
                sunriseTime,
                dhuhrTime,
                asrTime,
                sunsetTime,
                maghribTime,
                ishaTime,
                midnight
            )
        }

        lat = latitude
        lng = longitude
        timeZone = tZone

        val date = year.toString() + "-" + addZero(month) + "-" + addZero(day)
        val tomorrowDate = getOffsetDateByDay(1, date, SimpleDateFormat("yyyy-MM-dd", Locale.CHINA))
        val tomorrowYear = deleteZero(tomorrowDate.split("-")[0])
        val tomorrowMonth = deleteZero(tomorrowDate.split("-")[1])
        val tomorrowDay = deleteZero(tomorrowDate.split("-")[2])

        jDate = julianDate(year, month, day)
        val lonDiff = longitude / (15.0 * 24.0)
        jDate -= lonDiff
        val todayDoubleArray = computeDayTimes2()

        jDate = julianDate(tomorrowYear, tomorrowMonth, tomorrowDay)
        jDate -= lonDiff
        val tomorrowDoubleArray = computeDayTimes2()

        val sunset = todayDoubleArray[4]
        val midnightDouble = if (type == 0) {
            val sunrise = tomorrowDoubleArray[1]
            val diff = sunrise - sunset + 24
            sunset + (diff / 2)
        } else {
            val fajr = tomorrowDoubleArray[0]
            val diff = fajr - sunset + 24
            sunset + (diff / 2)
        }

        fajrTime = adjustTimesFormat2(todayDoubleArray[0])
        fajrTime += appendAmOrPm(todayDoubleArray[0])
        sunriseTime = adjustTimesFormat2(todayDoubleArray[1])
        sunriseTime += appendAmOrPm(todayDoubleArray[1])
        dhuhrTime = adjustTimesFormat2(todayDoubleArray[2])
        dhuhrTime += appendAmOrPm(todayDoubleArray[2])
        asrTime = adjustTimesFormat2(todayDoubleArray[3])
        asrTime += appendAmOrPm(todayDoubleArray[3])
        sunsetTime = adjustTimesFormat2(todayDoubleArray[4])
        sunsetTime += appendAmOrPm(todayDoubleArray[4])
        maghribTime = adjustTimesFormat2(todayDoubleArray[5])
        maghribTime += appendAmOrPm(todayDoubleArray[5])
        ishaTime = adjustTimesFormat2(todayDoubleArray[6])
        ishaTime += appendAmOrPm(todayDoubleArray[6])
        midnight = adjustTimesFormat2(midnightDouble % 24)
        midnight += appendAmOrPm(midnightDouble % 24)

        return mutableListOf(
            fajrTime,
            sunriseTime,
            dhuhrTime,
            asrTime,
            sunsetTime,
            maghribTime,
            ishaTime,
            midnight
        )
    }


    private fun appendAmOrPm(time: Double): String {
        return if (time >= 12.0) {
            "PM"
        } else {
            "AM"
        }
    }


    fun getDatePrayerTimes(
        year: Int, month: Int, day: Int,
        latitude: Double, longitude: Double, tZone: Double,
    ): ArrayList<String> {
        lat = latitude
        lng = longitude
        timeZone = tZone
        jDate = julianDate(year, month, day)
        val lonDiff = longitude / (15.0 * 24.0)
        jDate -= lonDiff
        return computeDayTimes()
    }

    // return prayer times for a given date
    fun getPrayerTimes(
        date: Calendar, latitude: Double,
        longitude: Double, tZone: Double,
    ): ArrayList<String> {
        val year = date[Calendar.YEAR]
        val month = date[Calendar.MONTH]
        val day = date[Calendar.DATE]
        return getDatePrayerTimes(year, month + 1, day, latitude, longitude, tZone)
    }


    fun getPrayerTimes(
        mYear: Int, mMonth: Int, mDay: Int, latitude: Double,
        longitude: Double, tZone: Double,
    ): ArrayList<String> {
        return getDatePrayerTimes(mYear, mMonth, mDay, latitude, longitude, tZone)
    }


    // set custom values for calculation parameters
    fun setCustomParams(params: DoubleArray) {
        for (i in 0..4) {
            if (params[i].toInt() == -1) {
                params[i] = methodParams[calcMethod]!![i]
                methodParams[custom] = params
            } else {
                methodParams[custom]!![i] = params[i]
            }
        }
        calcMethod = custom
    }

    // set the angle for calculating Fajr
    fun setFajrAngle(angle: Double) {
        val params = doubleArrayOf(angle, -1.0, -1.0, -1.0, -1.0)
        setCustomParams(params)
    }

    // set the angle for calculating Maghrib
    fun setMaghribAngle(angle: Double) {
        val params = doubleArrayOf(-1.0, 0.0, angle, -1.0, -1.0)
        setCustomParams(params)
    }

    // set the angle for calculating Isha
    fun setIshaAngle(angle: Double) {
        val params = doubleArrayOf(-1.0, -1.0, -1.0, 0.0, angle)
        setCustomParams(params)
    }

    // set the minutes after Sunset for calculating Maghrib
    fun setMaghribMinutes(minutes: Double) {
        val params = doubleArrayOf(-1.0, 1.0, minutes, -1.0, -1.0)
        setCustomParams(params)
    }

    // set the minutes after Maghrib for calculating Isha
    fun setIshaMinutes(minutes: Double) {
        val params = doubleArrayOf(-1.0, -1.0, -1.0, 1.0, minutes)
        setCustomParams(params)
    }

    // convert double hours to 24h format
    fun floatToTime24(time: Double): String {
        var time = time
        val result: String
        if (java.lang.Double.isNaN(time)) {
            return InvalidTime
        }
        time = fixhour(time + 0.5 / 60.0) // add 0.5 minutes to round
        val hours = floor(time).toInt()
        val minutes = floor((time - hours) * 60.0)
        result = if (hours in 0..9 && minutes >= 0 && minutes <= 9) {
            "0" + hours + ":0" + minutes.roundToInt()
        } else if (hours in 0..9) {
            "0" + hours + ":" + minutes.roundToInt()
        } else if (minutes in 0.0..9.0) {
            hours.toString() + ":0" + minutes.roundToInt()
        } else {
            hours.toString() + ":" + minutes.roundToInt()
        }
        return result
    }

    // convert double hours to 12h format
    fun floatToTime12(time: Double, noSuffix: Boolean): String {
        var time = time
        if (java.lang.Double.isNaN(time)) {
            return InvalidTime
        }
        time = fixhour(time + 0.5 / 60) // add 0.5 minutes to round
        var hours = Math.floor(time).toInt()
        val minutes = Math.floor((time - hours) * 60)
//        val suffix: String
        val result: String
//        suffix = if (hours >= 12) {
//            "pm"
//        } else {
//            "am"
//        }
        hours = (hours + 12 - 1) % 12 + 1
        /*hours = (hours + 12) - 1;
        int hrs = (int) hours % 12;
        hrs += 1;*/result = if (noSuffix == false) {
            if (hours >= 0 && hours <= 9 && minutes >= 0 && minutes <= 9) {
                ("0" + hours + ":0" + Math.round(minutes))
            } else if (hours >= 0 && hours <= 9) {
                "0" + hours + ":" + Math.round(minutes)
            } else if (minutes >= 0 && minutes <= 9) {
                hours.toString() + ":0" + Math.round(minutes)
            } else {
                hours.toString() + ":" + Math.round(minutes)
            }
        } else {
            if (hours >= 0 && hours <= 9 && minutes >= 0 && minutes <= 9) {
                "0" + hours + ":0" + Math.round(minutes)
            } else if (hours >= 0 && hours <= 9) {
                "0" + hours + ":" + Math.round(minutes)
            } else if (minutes >= 0 && minutes <= 9) {
                hours.toString() + ":0" + Math.round(minutes)
            } else {
                hours.toString() + ":" + Math.round(minutes)
            }
        }
        return result
    }

    // convert double hours to 12h format with no suffix
    fun floatToTime12NS(time: Double): String {
        return floatToTime12(time, true)
    }

    // ---------------------- Compute Prayer Times -----------------------
    // compute prayer times at given julian date
    fun computeTimes(times: DoubleArray): DoubleArray {
        val t = dayPortion(times)
        val Fajr = computeTime(
            180 - methodParams[calcMethod]!![0], t[0]
        )
        val Sunrise = computeTime(180 - 0.833, t[1])
        val Dhuhr = computeMidDay(t[2])
        val Asr = computeAsr((1 + asrJuristic).toDouble(), t[3])
        val Sunset = computeTime(0.833, t[4])
        val Maghrib = computeTime(
            methodParams[calcMethod]!![2], t[5]
        )
        val Isha = computeTime(
            methodParams[calcMethod]!![4], t[6]
        )
        return doubleArrayOf(Fajr, Sunrise, Dhuhr, Asr, Sunset, Maghrib, Isha)
    }

    fun computeDayTimes3(): DoubleArray {
        var times = doubleArrayOf(5.0, 6.0, 12.0, 13.0, 18.0, 18.0, 18.0) // default times
        for (i in 1..numIterations) {
            times = computeTimes(times)
        }
        //  times = adjustTimes(times)
        //  times = tuneTimes(times)
        return times
    }

    // compute prayer times at given julian date
    fun computeDayTimes2(): DoubleArray {
        var times = doubleArrayOf(5.0, 6.0, 12.0, 13.0, 18.0, 18.0, 18.0) // default times
        for (i in 1..numIterations) {
            times = computeTimes(times)
        }
        times = adjustTimes(times)
        times = tuneTimes(times)
        return times
    }


    // compute prayer times at given julian date
    fun computeDayTimes(): ArrayList<String> {
        println("$TAG_NAME computeDayTimes numIterations: $numIterations")

        var times = doubleArrayOf(5.0, 6.0, 12.0, 13.0, 18.0, 18.0, 18.0) // default times
        for (i in 1..numIterations) {
            times = computeTimes(times)
        }
        times = adjustTimes(times)
        times = tuneTimes(times)
        return adjustTimesFormat(times)
    }


    // adjust times in a prayer time array
    fun adjustTimes(times: DoubleArray): DoubleArray {
        var times = times
        for (i in times.indices) {
            times[i] += timeZone - lng / 15
        }


        times[2] += (dhuhrMinutes / 60).toDouble() // Dhuhr
        if (methodParams[calcMethod]!![1].toInt() == 1) {   // Maghrib
            times[5] = times[4] + methodParams[calcMethod]!![2] / 60
        }
        if (methodParams[calcMethod]!![3].toInt() == 1) // Isha
        {
            times[6] = times[5] + methodParams[calcMethod]!![4] / 60
        }
        if (adjustHighLats != none) {
            times = adjustHighLatTimes(times)
        }
        return times
    }

    // convert times array to given time format
    fun adjustTimesFormat(times: DoubleArray): ArrayList<String> {
        val result = ArrayList<String>()
        if (timeFormat == floating) {
            for (time in times) {
                result.add(time.toString())
            }
            return result
        }
        for (i in 0..6) {
            if (timeFormat == time12) {
                result.add(floatToTime12(times[i], false))
            } else if (timeFormat == time12NS) {
                result.add(floatToTime12(times[i], true))
            } else {
                result.add(floatToTime24(times[i]))
            }
        }
        return result
    }

    // convert times array to given time format
    fun adjustTimesFormat2(time: Double): String {
        var result = ""
        if (timeFormat == floating) {
            result = time.toString()
            return result
        }

        result = when (timeFormat) {
            time12 -> {
                floatToTime24(time)
            }
            time12NS -> {
                floatToTime24(time)
            }
            else -> {
                floatToTime24(time)
            }
        }
        return result
    }


    // adjust Fajr, Isha and Maghrib for locations in higher latitudes
    fun adjustHighLatTimes(times: DoubleArray): DoubleArray {
        val nightTime = timeDiff(times[4], times[1]) // sunset to sunrise

        // Adjust Fajr
        val FajrDiff = nightPortion(methodParams[calcMethod]!![0]) * nightTime
        if (java.lang.Double.isNaN(times[0]) || timeDiff(times[0], times[1]) > FajrDiff) {
            times[0] = times[1] - FajrDiff
        }

        // Adjust Isha
        val IshaAngle: Double =
            if (methodParams[calcMethod]!![3].toInt() == 0) methodParams[calcMethod]!![4] else 18.0
        val IshaDiff = nightPortion(IshaAngle) * nightTime
        if (java.lang.Double.isNaN(times[6]) || timeDiff(times[4], times[6]) > IshaDiff) {
            times[6] = times[4] + IshaDiff
        }

        // Adjust Maghrib
        val MaghribAngle: Double =
            if (methodParams[calcMethod]!![1].toInt() == 0) methodParams[calcMethod]!![2] else 4.0
        val MaghribDiff = nightPortion(MaghribAngle) * nightTime
        if (java.lang.Double.isNaN(times[5]) || timeDiff(times[4], times[5]) > MaghribDiff) {
            times[5] = times[4] + MaghribDiff
        }
        return times
    }

    // the night portion used for adjusting times in higher latitudes
    fun nightPortion(angle: Double): Double {
        var calc = 0.0
        if (adjustHighLats == angleBased) calc =
            angle / 60.0 else if (adjustHighLats == midNight) calc =
            0.5 else if (adjustHighLats == oneSeventh) calc = 0.14286
        return calc
    }

    // convert hours to day portions
    fun dayPortion(times: DoubleArray): DoubleArray {
        for (i in 0..6) {
            times[i] = times[i] / 24
        }
        return times
    }

    // Tune timings for adjustments
    // Set time offsets
    fun tune(offsetTimes: IntArray) {
        for (i in offsetTimes.indices) { // offsetTimes length
            // should be 7 in order
            // of Fajr, Sunrise,
            // Dhuhr, Asr, Sunset,
            // Maghrib, Isha
            offsets[i] = offsetTimes[i]
        }
    }

    fun tuneTimes(times: DoubleArray): DoubleArray {
        for (i in times.indices) {
            times[i] = times[i] + offsets[i] / 60.0
        }
        return times
    }

    companion object {
        const val TAG_NAME = "PrayTime"

        fun addZero(t: Int): String {
            return if (t < 10) "0$t" else t.toString()
        }

        fun deleteZero(str: String): Int {
            return if (str[0] == '0') {
                return str.substring(1, str.length).toInt()
            } else {
                str.toInt()
            }
        }
    }

    init {
        // Initialize vars
        calcMethod = 0
        asrJuristic = 0
        dhuhrMinutes = 0
        adjustHighLats = 1
        timeFormat = 0

        // Calculation Methods
        jafari = 0 // Ithna Ashari
        karachi = 1 // University of Islamic Sciences, Karachi
        iSNA = 2 // Islamic Society of North America (ISNA)
        mWL = 3 // Muslim World League (MWL)
        makkah = 4 // Umm al-Qura, Makkah
        egypt = 5 // Egyptian General Authority of Survey
        tehran = 6 // Institute of Geophysics, University of Tehran
        muis = 7 //Majlis Ugama Islam Singapura.

        //ring 2.0 新增惯例
        jakim = 8 // Calculation method from Jabatan Kemajuan Islam Malaysia.
        kemenag = 9 // Calculation method from Kementerian Agama Republik Indonesia.
        diyanet = 10 // Calculation method from Turkey's Diyanet İşleri Başkanlığı.
        dsmr = 11 // Calculation method from Spiritual Administration of Muslims of Russia.
        dumsr = 12 // Calculation method from The Muslim Spiritual Board of Tatarstan.
        gaiae = 13 // Calculation method from General Authority of Islamic Affairs And Endowments.
        gulf =
            14 // Calculation method that often used by countries in Gulf region like UAE and Kuwait.
        algeria = 15 // Calculation method from Algerian Ministry of Religious Affairs and Wakfs.
        uoif = 16 // Calculation method from Union Des Organisations Islamiques De France.
        france15 = 17 // Calculation method for France region with Fajr and Isha both at 15°.
        france18 =
            18 // Another calculation method for France region with Fajr and Isha both at 18°.
        tunisia = 19 // Calculation method from Tunisian Ministry of Religious Affairs.
        london =
            20 // Calculation method from London Central Mosque Trust &amp; The Islamic Cultural Centre.
        custom = 21 // Custom Setting

        // Juristic Methods
        shafii = 0 // Shafii (standard)
        hanafi = 1 // Hanafi

        // Adjusting Methods for Higher Latitudes
        none = 0 // No adjustment
        midNight = 1 // middle of night
        oneSeventh = 2 // 1/7th of night
        angleBased = 3 // angle/60th of night

        // Time Formats
        time24 = 0 // 24-hour format
        time12 = 1 // 12-hour format
        time12NS = 2 // 12-hour format with no suffix
        floating = 3 // floating point number

        // Time Names
        timeNames = ArrayList()
        timeNames.add(R.string.prayer_fajr)
        timeNames.add(R.string.prayer_sunrise)
        timeNames.add(R.string.prayer_dhuhr)
        timeNames.add(R.string.prayer_asr)
        timeNames.add(R.string.prayer_sunset)
        timeNames.add(R.string.prayer_maghrib)
        timeNames.add(R.string.prayer_isha)
        timeNames.add(R.string.midnight)
        InvalidTime = "-----" // The string used for invalid times

        // --------------------- Technical Settings --------------------
        numIterations = 1 // number of iterations needed to compute
        // times

        // ------------------- Calc Method Parameters --------------------

        // Tuning offsets {fajr, sunrise, dhuhr, asr, sunset, maghrib, isha}
        offsets = IntArray(7)
        offsets[0] = 0
        offsets[1] = 0
        offsets[2] = 0
        offsets[3] = 0
        offsets[4] = 0
        offsets[5] = 0
        offsets[6] = 0

        /*
         *
         * fa : fajr angle
         *
         * ms : maghrib selector (0 = angle; 1 = minutes after
         * sunset)
         *
         * mv : maghrib parameter value (in angle or minutes)
         *
         * is : isha
         * selector (0 = angle; 1 = minutes after maghrib)
         *
         * iv : isha parameter value (in angle or minutes)
         */methodParams = HashMap()

        // Jafari
        val Jvalues = doubleArrayOf(16.0, 0.0, 4.0, 0.0, 14.0)
        methodParams[Integer.valueOf(jafari)] = Jvalues

        // Karachi
        val Kvalues = doubleArrayOf(18.0, 1.0, 0.0, 0.0, 18.0)
        methodParams[Integer.valueOf(karachi)] = Kvalues

        // ISNA
        val Ivalues = doubleArrayOf(15.0, 1.0, 0.0, 0.0, 15.0)
        methodParams[Integer.valueOf(iSNA)] = Ivalues

        // MWL
        val MWvalues = doubleArrayOf(18.0, 1.0, 0.0, 0.0, 17.0)
        methodParams[Integer.valueOf(mWL)] = MWvalues

        // Makkah
        val MKvalues = doubleArrayOf(18.5, 1.0, 0.0, 1.0, 90.0)
        methodParams[Integer.valueOf(makkah)] = MKvalues

        // Egypt
        val Evalues = doubleArrayOf(19.5, 1.0, 0.0, 0.0, 17.5)
        methodParams[Integer.valueOf(egypt)] = Evalues

        // Tehran
        val Tvalues = doubleArrayOf(17.7, 0.0, 4.5, 0.0, 14.0)
        methodParams[Integer.valueOf(tehran)] = Tvalues

        // Muis
        val muisValues = doubleArrayOf(20.0, 1.0, 0.0, 0.0, 18.0)
        methodParams[Integer.valueOf(muis)] = muisValues

        //ring 2.0 新增惯例

        // jakim
        val jakimValues = doubleArrayOf(20.0, 1.0, 0.0, 0.0, 18.0)
        methodParams[Integer.valueOf(jakim)] = jakimValues

        // kemenag
        val kemenagValues = doubleArrayOf(20.0, 1.0, 0.0, 0.0, 18.0)
        methodParams[Integer.valueOf(kemenag)] = kemenagValues

        // diyanet
        val diyanetValues = doubleArrayOf(18.0, 1.0, 0.0, 0.0, 17.0)
        methodParams[Integer.valueOf(diyanet)] = diyanetValues

        // dsmr
        val dsmrValues = doubleArrayOf(16.0, 1.0, 0.0, 0.0, 15.0)
        methodParams[Integer.valueOf(dsmr)] = dsmrValues

        // dumsr
        val dumsrValues = doubleArrayOf(18.0, 1.0, 0.0, 0.0, 15.0)
        methodParams[Integer.valueOf(dumsr)] = dumsrValues

        // gaiae
        val gaiaeValues = doubleArrayOf(19.5, 1.0, 0.0, 1.0, 90.0)
        methodParams[Integer.valueOf(gaiae)] = gaiaeValues

        // gulf
        val gulfValues = doubleArrayOf(19.5, 1.0, 0.0, 1.0, 90.0)
        methodParams[Integer.valueOf(gulf)] = gulfValues

        // algeria
        val algeriaValues = doubleArrayOf(18.0, 1.0, 0.0, 0.0, 17.0)
        methodParams[Integer.valueOf(algeria)] = algeriaValues

        // uoif
        val uoifValues = doubleArrayOf(12.0, 1.0, 0.0, 0.0, 12.0)
        methodParams[Integer.valueOf(uoif)] = uoifValues

        // france15
        val france15Values = doubleArrayOf(15.0, 1.0, 0.0, 0.0, 15.0)
        methodParams[Integer.valueOf(france15)] = france15Values

        // france18
        val france18Values = doubleArrayOf(18.0, 1.0, 0.0, 0.0, 18.0)
        methodParams[Integer.valueOf(france18)] = france18Values

        // tunisia
        val tunisiaValues = doubleArrayOf(18.0, 1.0, 0.0, 0.0, 18.0)
        methodParams[Integer.valueOf(tunisia)] = tunisiaValues

        // london
        val londonValues = doubleArrayOf(15.0, 1.0, 0.0, 0.0, 15.0)
        methodParams[Integer.valueOf(london)] = londonValues

        custom = 21 // Custom Setting
        // custom
        val cValues = doubleArrayOf(20.0, 1.0, 0.0, 0.0, 18.0)
        methodParams[Integer.valueOf(custom)] = cValues

        //新增固定惯例
        val type = object : TypeToken<Map<String, MutableList<String>>>() {}.type
        ingushetiaData = Gson().fromJson(getDataFromJson("ingushetia.json"), type)
        bruneikheuData = Gson().fromJson(getDataFromJson("KheuBrunei.json"), type)


    }

    /**
     * 解析固定惯例数据
     */
    private fun getDataFromJson(fileName: String): String {
        val stringBuffer = StringBuffer()
        try {
            val assetsManager: AssetManager = AppUtils.getApp().assets
            val bufferedReader =
                BufferedReader(InputStreamReader(assetsManager.open(fileName)))
            var line: String?
            while (bufferedReader.readLine().also { line = it } != null) {
                stringBuffer.append(line)
            }
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return stringBuffer.toString()
    }

    /**
     * 获取指定日期之前/之后amount天的日期
     * 如 amount = 2 currentDate_yMd = "2022-06-30"
     * 得到的结果就是  “2022-07-02”
     */
    fun getOffsetDateByDay(
        amount: Int,
        currentDate_yMd: String,
        dateFormat: SimpleDateFormat
    ): String {
        val currentDate = dateFormat.parse(currentDate_yMd)
        val calendar = Calendar.getInstance()
        calendar.time = currentDate!!
        calendar.add(Calendar.DAY_OF_MONTH, amount)
        return dateFormat.format(calendar.time)
    }
}
